<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>文档</title>
  <link href="./css/semantic/semantic.min.css" rel="stylesheet">
  <link href="./css/markdown.css" rel="stylesheet">
    <link href="./css/wtxynavigation.css" rel="stylesheet">
    <script src="./js/jquery.min.js"></script>
    <script type="text/javascript" src="./js/include.js"></script>
  <style>
    body{padding-top:0;background:#f7f7f7}
    body>.ui.container{width:100%;padding:0}
    #project-title{background:#6e6e6e;width:100%;line-height:3.572rem;color:#fff;font-size:1.2rem}
    #project-title .title-wrap{font-weight:700;padding:0 12px;width:100%;max-width:1366px!important;min-width:900px;margin:0 auto}
    #project-title .left,#project-title right{display:inline-block}
    #project-title .right{float:right}
    #project-title .right .wiki-actions .menu{left:auto;right:0;margin-top:.4em!important}
    #wiki-preview-container{width:100%;max-width:1366px!important;min-width:900px;padding:0;border:1px solid #e3e9ed;border-top:0}
    #wiki-preview>.ui.segment{margin-top:0;padding:1rem;padding-left:0;border:0}
    .hover-places{display:inline-block}
    #page-detail{width:60%;margin:0 auto}
    #page-detail .title{font-weight:700;font-size:24px;padding:12px 0;border-bottom:1px solid #ccc}
    #page-detail .content{margin-top:15px}
    .content p{word-wrap:break-word}
    #wiki-parent,#wiki-title,#wikis{display:none}
    .ui.grid{padding:0!important}
    #ex-all{cursor:pointer;color:#537c8d}
  </style>
</head>
<body>
  <div class="ui container">
    <div id="project-title">
      <div class="title-wrap">
        <div class="left">
          <i class="icon eye"></i>
          文档预览:
          mianmSDK
        </div>
        <div class="right">
          Export by Gitee
        </div>
      </div>
    </div>

    <div class="ui container" id="wiki-preview-container">
      <div id="wiki-preview">
        <div class="ui segment">
          <div id="page-detail" class="markdown-body">
            <div class='title'>4.iOS数据持久化方案有哪些?</div><div class='content'><h1>
<a id="4ios数据持久化方案有哪些" class="anchor" href="#4ios%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96%E6%96%B9%E6%A1%88%E6%9C%89%E5%93%AA%E4%BA%9B"></a>4.iOS数据持久化方案有哪些?</h1>
<h1>
<a id="答" class="anchor" href="#%E7%AD%94"></a>答:</h1>
<ol>
<li>
<strong>plist存储</strong>: 使用<code>XML</code>键值对持久化，它适用于少量且数据基本不怎么改变的情况。</li>
<li>
<strong>偏好设置</strong>: 使用<code>NSUserDefalut</code>持久化，专门用来保存应用程序的配置信息的，一般不要在偏好设置中保存其他数据。</li>
<li>
<strong>归档序列化存储</strong>: 使用二进制序列化持久化，只要遵循了<code>NSCoding</code>协议的对象都可以通过它实现序列化。</li>
<li>
<strong>沙盒存储</strong>: 持久化在<code>Document</code>目录下，一般持久化一些文件，比如图片，音频，视频等，文件沙盒存储主要存储非机密数据。</li>
<li>
<strong>本地数据库存储</strong>: 适合储存大规模数据，管理方便，不过操作稍微复杂一些。</li>
</ol>
<h2>
<a id="描述" class="anchor" href="#%E6%8F%8F%E8%BF%B0"></a>描述</h2>
<p><a href="https://www.cnblogs.com/XYQ-208910/p/7687512.html">文章来至博客园-XYQ全哥</a></p>
<h2>
<a id="1-plist存储" class="anchor" href="#1-plist%E5%AD%98%E5%82%A8"></a>1. plist存储</h2>
<p>plist文件，即属性列表文件，全名是Property List，这种文件的扩展名为.plist，因此，通常被叫做plist文件。</p>
<h3>
<a id="11-作用" class="anchor" href="#11-%E4%BD%9C%E7%94%A8"></a>1.1 作用：</h3>
<p>它是一种用来存储串行化后的对象的文件，在iOS开发中通常用来存储用户设置，还可以用于存储程序中经常用到而不经常改动的数据。</p>
<h3>
<a id="12-什么数据适合存储" class="anchor" href="#12-%E4%BB%80%E4%B9%88%E6%95%B0%E6%8D%AE%E9%80%82%E5%90%88%E5%AD%98%E5%82%A8"></a>1.2 什么数据适合存储？</h3>
<ul>
<li>能存储<code>NSString</code>、<code>NSArray</code>、<code>NSDictionary</code>、<code>NSData</code>、<code>NSDate</code>、<code>NSNumber</code>、<code>Boolean</code>
</li>
<li><strong>不能存储自定义对象</strong></li>
</ul>
<h3>
<a id="13-存到什么地方" class="anchor" href="#13-%E5%AD%98%E5%88%B0%E4%BB%80%E4%B9%88%E5%9C%B0%E6%96%B9"></a>1.3 存到什么地方？</h3>
<p>写入创建的.plist文件中</p>
<h3>
<a id="14-使用场景" class="anchor" href="#14-%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"></a>1.4 使用场景？</h3>
<p>plist常用于存储长时间不容易发生变化的数据，例如省市列表、车辆名称列表之类的数据等，这些数据可以保存在 plist 文件里，所以plist适用于存储小型数据，不推荐用plist做缓冲。</p>
<h3>
<a id="15-如何使用" class="anchor" href="#15-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8"></a>1.5 如何使用？</h3>
<p>存储</p>
<pre><code>
[dict writeToFile:filePath atomically:YES]; // 字典
[array writeToFile:filePath atomically:YES]; // 数组
[string writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; // 字符串


</code></pre>
<p>获取</p>
<pre><code>
NSArray *arr = [NSArray arrayWithContentsOfFile:filePath];  // 数组
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath]; // 字典 
NSString *string = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; // 字符串 　　 


</code></pre>
<h3>
<a id="16-有什么缺点" class="anchor" href="#16-%E6%9C%89%E4%BB%80%E4%B9%88%E7%BC%BA%E7%82%B9"></a>1.6 有什么缺点？</h3>
<p>因为所有的数据都放在root dictionary里,每次读取都要把整个root dictionary取出来再取需要的对象.如果plist文件缓存了几十M的数据.这样很费内存和时间。</p>
<h2>
<a id="2-偏好存储" class="anchor" href="#2-%E5%81%8F%E5%A5%BD%E5%AD%98%E5%82%A8"></a>2. 偏好存储</h2>
<h3>
<a id="21-定义" class="anchor" href="#21-%E5%AE%9A%E4%B9%89"></a>2.1 定义：</h3>
<ul>
<li>
<p><code>User Defaults</code> 顾名思义就是一个用户为系统以及程序设置的默认值。</p>
</li>
<li>
<p>每个用户都有自己的一套数据，用户和用户之间没法共享的。在苹果的API中，提供了一个类去存储用户的偏好设置。</p>
</li>
<li>
<p>这个方法推荐只存储用户的偏好设置，不要存储一些字典、数组之类的。</p>
</li>
</ul>
<h3>
<a id="22-作用" class="anchor" href="#22-%E4%BD%9C%E7%94%A8"></a>2.2 作用：</h3>
<ul>
<li>
<p>很多<code>iOS</code>应用都支持偏好设置，比如保存用户名、密码、字体大小等设置。</p>
</li>
<li>
<p><code>iOS</code>提供了一套标准的解决方案来为应用加入偏好设置功能，就是每一个<code>app</code>都有一个<code>plist</code>文件专门用以保存偏好设置数据。</p>
</li>
<li>
<p>每个应用都有个<code>NSUserDefaults</code>实例，通过它来存取偏好设置。</p>
</li>
</ul>
<h3>
<a id="23-问题" class="anchor" href="#23-%E9%97%AE%E9%A2%98"></a>2.3 问题：</h3>
<h4>
<a id="231-什么数据适合存储" class="anchor" href="#231-%E4%BB%80%E4%B9%88%E6%95%B0%E6%8D%AE%E9%80%82%E5%90%88%E5%AD%98%E5%82%A8"></a>2.3.1 什么数据适合存储？</h4>
<p>可以存储<code>OC</code>定义的所有数据类型，包括对象(系统和自定义的)类型、基本数据类型，如<code>NSInteger</code>等。</p>
<h4>
<a id="232-存到什么地方" class="anchor" href="#232-%E5%AD%98%E5%88%B0%E4%BB%80%E4%B9%88%E5%9C%B0%E6%96%B9"></a>2.3.2 存到什么地方？</h4>
<p><code>NSUserDefault</code> 本地保存的位置是<code>Library/Preferences</code> 这个目录下的 plist 文件。</p>
<h4>
<a id="233-使用场景" class="anchor" href="#233-%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"></a>2.3.3 使用场景？</h4>
<ul>
<li>
<p>在App中，有时候我们需要将一些信息进行短期的保存，方便用户下次更方便使用App，减少多余的操作，增强用户体验。</p>
</li>
<li>
<p>比如，保存用户名、字体大小、是否自动登录等。</p>
</li>
</ul>
<h4>
<a id="234-如何使用" class="anchor" href="#234-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8"></a>2.3.4 如何使用？</h4>
<p>存储</p>
<pre><code>
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; [defaults setObject: forKey:]; 
[defaults setInteger:forKey:]; 
[defaults setDouble: forKey:]; 
[defaults setObject: forKey:];
[defaults synchronize];

</code></pre>
<p>获取</p>
<pre><code>
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
[defaults objectForKey:];
[defaults objectForKey:];
[defaults integerForKey:];
[defaults doubleForKey:];

</code></pre>
<h4>
<a id="235-注意事项" class="anchor" href="#235-%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9"></a>2.3.5 注意事项？</h4>
<ul>
<li>
<p>偏好设置是专门用来保存应用程序的配置信息的, 一般情况不要在偏好设置中保存其他数据。</p>
</li>
<li>
<p>如果利用系统的偏好设置来存储数据, 默认就是存储在<code>Preferences</code>文件夹下面的，偏好设置会将所有的数据都保存到同一个文件中。</p>
</li>
<li>
<p>使用偏好设置对数据进行保存之后, 它保存到系统的时间是不确定的，会在将来某一时间点自动将数据保存到<code>Preferences</code>文件夹下面，如果需要即刻将数据存储，可以使用<code>[defaults synchronize]</code>;</p>
</li>
<li>
<p>所有的信息都写在一个文件中，对比简单的<code>plist</code>可以保存和读取基本的数据类型。</p>
</li>
</ul>
<h2>
<a id="3归档序列化存储" class="anchor" href="#3%E5%BD%92%E6%A1%A3%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%98%E5%82%A8"></a>3.归档序列化存储</h2>
<h3>
<a id="31-定义" class="anchor" href="#31-%E5%AE%9A%E4%B9%89"></a>3.1 定义：</h3>
<p>对象归档是iOS中数据持久化的一种方式。 归档是指另一种形式的二进制序列化，但它是任何对象都可以实现的更常规的类型。</p>
<h3>
<a id="32-作用" class="anchor" href="#32-%E4%BD%9C%E7%94%A8"></a>3.2 作用：</h3>
<p>使用对模型对象进行归档的技术可以轻松将复杂的对象写入文件，然后再从中读取它们。</p>
<h3>
<a id="33-问题" class="anchor" href="#33-%E9%97%AE%E9%A2%98"></a>3.3 问题：</h3>
<h4>
<a id="331-什么数据适合存储" class="anchor" href="#331-%E4%BB%80%E4%B9%88%E6%95%B0%E6%8D%AE%E9%80%82%E5%90%88%E5%AD%98%E5%82%A8"></a>3.3.1 什么数据适合存储？</h4>
<p>　　 要使用对象归档，则归档的对象所属类中实现的每个属性都是标量，或者都是遵循<code>NSCoding</code>协议和<code>NSCopying</code>协议的某个类的实例对象。</p>
<h4>
<a id="332-存到什么地方" class="anchor" href="#332-%E5%AD%98%E5%88%B0%E4%BB%80%E4%B9%88%E5%9C%B0%E6%96%B9"></a>3.3.2 存到什么地方？</h4>
<p>　　  对象归档后将得到一个后缀为<code>.archive</code>的文件,数据就保存在了这个文件中。</p>
<h4>
<a id="333-使用场景" class="anchor" href="#333-%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"></a>3.3.3 使用场景？</h4>
<p>　　  定义某个实例，如果需要持久化该实例从而方便以后使用它的属性值，同时可以随意更改该实例的属性值，推荐在给实例初始化的同时直接使用归档进行存储。</p>
<h3>
<a id="334-如何使用" class="anchor" href="#334-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8"></a>3.3.4 如何使用？</h3>
<p>遵循<strong>NSCoding</strong>协议</p>
<ul>
<li>NSCoding中声明了两个方法，其中一个用于将对象编码到归档中，另一个方法对归档解码来创建一个新对象。</li>
<li>归档时要实现的方法为：<code>-(void)encodeWithCoder:(NSCoder *)aCoder;</code>
</li>
<li>可以使用 <strong>KVC(Key-Value Coding,键值编码)</strong> 对对象和原生数据类型进行编码和解码。</li>
<li>若要对对象进行归档，必须使用正确的编码方法将所有实例变量编码成<code>encoder</code>。</li>
<li>在解码时，实现一个通过<code>NSCoder</code>解码的对象初始化方法，就可以恢复之前归档的对象。</li>
<li>解码时要实现的方法为：<code>-(id)initWithCoder:(NSCoder *)aDecode;</code>
</li>
</ul>
<p>实现<strong>NSCopying</strong>协议</p>
<ul>
<li>除了要遵循<code>NSCoding</code>协议外，还要求要使用归档的类实现<code>NSCoping</code>协议。 用来复制对象。</li>
<li>这个协议中有一个<code>copywithZone</code>方法：<code>- (id)copyWithZone:(NSZone *)zone;</code>
</li>
<li>其实现与<code>initWitheCoder</code>非常相似，只需创建一个同一类的新实例，然后将新实例的所有属性都设置为与该对象属性相同的值。</li>
</ul>
<p><strong>存储</strong></p>
<pre><code>
+ (BOOL)archiverWithObject:(id&lt;NSCoding&gt;)obj forKey:(NSString *)key {
    NSString *filePath = [[self pathForArchiver] stringByAppendingPathComponent:key];
    NSMutableData *archiverData = [NSMutableData data];
    NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiverData];
    [archiver encodeObject:obj forKey:key];
    [archiver finishEncoding];
    if ([archiverData writeToFile:filePath atomically:YES]) {
        return YES;
    }else{
        return NO;
    }
}
</code></pre>
<p><strong>获取</strong></p>
<pre><code>
+ (id&lt;NSCoding&gt;)unarchiverWithKey:(NSString *)key {
    NSString *filePath = [[self pathForArchiver] stringByAppendingPathComponent:key];
    NSData *unarchiverData = [NSData dataWithContentsOfFile:filePath];
    NSKeyedUnarchiver *unachiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:unarchiverData];
    id&lt;NSCoding&gt; obj = [unachiver decodeObjectForKey:key];
    return obj;
}

</code></pre>
<h3>
<a id="335-有什么缺点" class="anchor" href="#335-%E6%9C%89%E4%BB%80%E4%B9%88%E7%BC%BA%E7%82%B9"></a>3.3.5 有什么缺点？</h3>
<p>当待存储的实例具有成百上千个属性的时候，单纯的一个个去序列化属性值耗时又费力。(当然可以借助runtime机制解决这个缺点,MJExtension这个框架就是这个原理)</p>
<h2>
<a id="4-document沙盒存储" class="anchor" href="#4-document%E6%B2%99%E7%9B%92%E5%AD%98%E5%82%A8"></a>4. Document沙盒存储</h2>
<h3>
<a id="41-定义" class="anchor" href="#41-%E5%AE%9A%E4%B9%89"></a>4.1 定义：</h3>
<ul>
<li>
<p>每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录)，与其他文件系统隔离。</p>
</li>
<li>
<p>应用必须待在自己的沙盒里，其他应用不能访问该沙盒。</p>
</li>
<li>
<p>沙盒的本质就是一个文件夹，名字是随机分配的。</p>
</li>
</ul>
<h3>
<a id="42-目录" class="anchor" href="#42-%E7%9B%AE%E5%BD%95"></a>4.2 目录：</h3>
<ul>
<li>
<strong>Application</strong>：存放程序源文件，上架前经过数字签名，上架后不可修改。</li>
<li>
<strong>Documents</strong>: 保存应⽤运行时生成的需要持久化的数据,<code>iTunes</code>同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录。</li>
<li>
<strong>tmp</strong>: 保存应⽤运行时所需的临时数据,使⽤完毕后再将相应的文件从该目录删除。应用 没有运行时,系统也可能会清除该目录下的文件。<code>iTunes</code>同步设备时不会备份该目录。</li>
<li>
<strong>Library/Caches</strong>: 保存应用运行时⽣成的需要持久化的数据,<code>iTunes</code>同步设备时不会备份 该目录。⼀一般存储体积大、不需要备份的非重要数据，比如网络数据缓存存储到<code>Caches</code>下</li>
<li>
<strong>Library/Preference</strong>: 保存应用的所有偏好设置，如<code>iOS</code>的<code>Settings</code>(设置) 应⽤会在该目录中查找应⽤的设置信息。<code>iTunes</code>同步设备时会备份该目录。</li>
</ul>
<pre><code>
// 获取程序的Home目录 
NSString  *path = NSHomeDirectory(); // 获取Document目录 
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)  fristObject]; // 获取Cache目录 
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) fristObject]; // 获取Library目录 
NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) fristObject]; // 获取Tmp目录 
NSString *path = NSTemporaryDirectory(); 

</code></pre>
<h3>
<a id="43-作用" class="anchor" href="#43-%E4%BD%9C%E7%94%A8"></a>4.3 作用：</h3>
<ul>
<li>
<p>用来存储和备份稍微较大的不是很重要的数据，比如<strong>缓存图片</strong>、<strong>音频</strong>、<strong>视频</strong>等，最典型的<code>SDWebImage</code>缓存图片的框架。</p>
</li>
<li>
<p>当然缓存的时间长短根据开发者选择持久化的目录路径有关。</p>
</li>
</ul>
<h3>
<a id="44-问题" class="anchor" href="#44-%E9%97%AE%E9%A2%98"></a>4.4 问题：</h3>
<ul>
<li>
<ol>
<li>什么数据适合存储？</li>
</ol>
<ul>
<li>图片、音频、视频、文本等</li>
</ul>
</li>
<li>
<ol start="2">
<li>存到什么地方？</li>
</ol>
<ul>
<li>写入创建的.txt、.data等任意扩展名的文件中</li>
</ul>
</li>
<li>
<ol start="3">
<li>使用场景？</li>
</ol>
<ul>
<li>当App中涉及到电子书阅读、听音乐、看视频、刷图片列表等时，推荐使用沙盒存储。</li>
<li>因为这可以极大的节约用户流量，而且也增强了app的体验效果。</li>
</ul>
</li>
<li>
<ol start="4">
<li>如何使用？</li>
</ol>
</li>
</ul>
<p><strong>写入文件</strong></p>
<pre><code>NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)  fristObject];             
NSArray *array = [[NSArray alloc] initWithObjects:@"内容",@"content",nil];            
NSString *filePath = [path stringByAppendingPathComponent:@"testFile.txt"];            
[array writeToFile:filePath atomically:YES];


</code></pre>
<p><strong>读取文件</strong></p>
<pre><code>
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) fristObject];            
NSString *filePath = [path stringByAppendingPathComponent:@"testFile.txt"];            
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];

</code></pre>
<h2>
<a id="5-本地数据库存储" class="anchor" href="#5-%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%98%E5%82%A8"></a>5. 本地数据库存储</h2>
<h3>
<a id="51-定义" class="anchor" href="#51-%E5%AE%9A%E4%B9%89"></a>5.1 定义：</h3>
<ul>
<li>
<p>数据库是一种常用的通过表进行数据存储的方式，表与表之间可以有一对一、一对多的联系，使用外键级联可以达到多表存取数据的目的。</p>
</li>
<li>
<p>在iOS中，目前有三种熟知数据库，分别是<code>Sqlite</code>、<code>CoreData</code>、<code>FMDB</code>，用的比较多的是<code>FMDB+Sqlite</code>结合的方式。</p>
</li>
</ul>
<h3>
<a id="52-特点" class="anchor" href="#52-%E7%89%B9%E7%82%B9"></a>5.2 特点：</h3>
<p><strong>Sqlite</strong></p>
<ul>
<li>
<p>是基于c语言开发的数据库，代码繁琐。</p>
</li>
<li>
<p>用c语言对数据库执行操作，访问。</p>
</li>
<li>
<p>sqlite是动态的数据库类型，即存储的时候是一种类型，使用的时候可以存储为其他类型。</p>
</li>
<li>
<p>在OC中不是可视化，不易理解。</p>
</li>
<li>
<p>建立连接之后可以不关闭连接。</p>
</li>
</ul>
<p><strong>CoreData</strong></p>
<ul>
<li>
<p>可视化，且具有undo/redo能力。</p>
</li>
<li>
<p>可以实现多种文件格式： * NSSQLiteStoreType  、 * NSBinaryStoreType  、* NSInMemoryStoreType  、* NSXMLStoreTyp。</p>
</li>
<li>
<p>苹果官方API支持，与iOS结合更紧密。</p>
</li>
</ul>
<p><strong>FMDB</strong></p>
<ul>
<li>
<p>FMDB以面向OC的方式封装了SQLite的C语言API。</p>
</li>
<li>
<p>使用起来更加面向对象，省去了很多麻烦、冗余的C语言代码。</p>
</li>
<li>
<p>对比苹果自带的Core  Data框架，更加轻量级和灵活。</p>
</li>
<li>
<p>提供了多线程安全的数据库操作方法，有效地防止数据混乱。</p>
</li>
</ul>
<h3>
<a id="53-作用" class="anchor" href="#53-%E4%BD%9C%E7%94%A8"></a>5.3 作用：</h3>
<p>用来进行大数据量的存储工作，不仅仅是容量大，而且通过索引查找速度很快，在App中这个是基本的功能。</p>
<h3>
<a id="54-问题" class="anchor" href="#54-%E9%97%AE%E9%A2%98"></a>5.4 问题：</h3>
<ol>
<li>什么数据适合存储？</li>
</ol>
<p>开发者定义的类的实例对象，该对象拥有的属性可以是任何类型，例如数字、 字符、 日期、 图片等等。</p>
<ol start="2">
<li>存到什么地方？</li>
</ol>
<p>写入创建的.sqlite、.db等任意扩展名的文件中</p>
<ol start="3">
<li>使用场景？</li>
</ol>
<p>在App中有大量的数据在没有网络的情况下仍然需要显示时，此时推荐使用本地数据库缓存这些数据。</p>
<ol start="4">
<li>如何使用？</li>
</ol>
<p><strong>Sqlite</strong></p>
<ul>
<li>
<p>新建项目时,先导入系统框架(C语言). （<code>libsqlite3</code>）</p>
</li>
<li>
<p>头文件<code>#import&lt;sqlite3.h&gt;</code></p>
</li>
<li>
<p><code>sqlite3_open(fileName.UTF8String, &amp;_db);</code></p>
<ul>
<li>//打开或者创建一个数据 ，<code>*_db</code>自己定义一个<code>sqlite3</code>的成员变量.进行增删改查时要用</li>
</ul>
</li>
<li>
<p><code>sqlite3_exec(_db, sql, NULL, NULL,&amp;error);</code></p>
<ul>
<li>//不带结果集的语句，只是对表做操作，不会返回出结果,*该函数可进行<code>insert</code>,<code>delete</code>,<code>update</code>操作.</li>
</ul>
</li>
<li>
<p><code>sqlite3_prepare_v2(_db, sql, -1, &amp;stmt, NULL);</code></p>
<ul>
<li>//做查询前准备,检测<code>SQL</code>语句是否正确.(查询操作<code>select</code>. 带结果集的查询语句，会返回出结果，从表中查询到的数据都会放到<code>stmt</code>结构体中)</li>
</ul>
</li>
<li>
<p><code>sqlite3_step(stmt) </code></p>
<ul>
<li>//提取查询到的数据,一次提取一条,通过循环可以取出所有数据</li>
</ul>
</li>
<li>
<p><code>sqlite3_column_text(stmt, 0)</code></p>
<ul>
<li>//取出第0列的数据.</li>
</ul>
</li>
<li>
<p><code>sqlite3_close(sqlite3 *);</code> //关闭数据库</p>
</li>
</ul>
<p><strong>源码</strong></p>
<pre><code>
NSString *sqlStr = @"INSERT OR REPLACE INTO note (cdate,content) VALUES (?,?)";
sqlite3_stmt *statement; //预处理过程,产生结果集
if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &amp;statement,
NULL) == SQLITE_OK)
 {
 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *nsdate = [dateFormatter stringFromDate:model.date]; //绑定参数开始
    sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL);  sqlite3_bind_text(statement, 2, [model.content UTF8String], -1,    NULL); //执行插入
if (sqlite3_step(statement) != SQLITE_DONE)
 { 
NSAssert(NO, @"插入数据失败。"); }
 }
} //清理结果集,防止内存泄露
sqlite3_finalize(statement);

</code></pre>
<p><strong>CoreData</strong></p>
<ul>
<li>
<p>创建项目时,勾选<code>CoreData</code>选项。</p>
</li>
<li>
<p>此时项目文件中多了一个<code>CoreData___.xcdatamodel</code>文件，选中该文件，进入其创建数据库表格界面，在界面的左下角点击<code>Add  Entity</code>添加实体对象，并设置该对象的类名；与此同时，在AppDeletegate类中，自动声明和定义了需要的三个对象<code>ManagedObjectModel</code>，<code>PersistentStoreCoordinator</code>，<code>ManagedObjectContext</code>  ，并且自动封装了大量的<code>sqlite</code>的函数方法。</p>
</li>
<li>
<p>在<code>attributes</code>选项处添加该实体对象的属性。</p>
</li>
<li>
<p>选中该实体类，在模拟器选项上点击<code>Editor</code>下的<code>createManagedObjectContext</code>  subclass..创建<code>ManagedObjectContext</code>的子类。</p>
</li>
<li>
<p>这个子类中，编译器自动生成了所添加的所有属性。</p>
</li>
<li>
<p>在应用程序代理类中用代码对数据库进行操作。</p>
</li>
</ul>
<pre><code>
//获取实体对象
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:@“ClassName” inManagedObjectContext:self.managedObjectContext]; //创建请求对象 
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“ClassName”]; //创建排序对象
NSSortDescriptor *ageSort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES]
[request setSortDescriptors:@[ageSort]]; //取出所有的数据
NSArray *fetcheObjects = [self.managedObjectContext executeFetchRequest:request error:&amp;error]

</code></pre>
<p><strong>FMDB</strong></p>
<ul>
<li>创建或打开</li>
</ul>
<pre><code>
self.db = [FMDatabase databaseWithPath:fileName]; //创建数据库

[self.db open]；//打开数据库

</code></pre>
<ul>
<li>查询语句：</li>
</ul>
<pre><code>
- (FMResultSet *)executeQuery:(NSString*)sql, ...　//返回结果集

- (BOOL)next； //遍历

- { type }ForColumnIndex：(int)columnIdx  //取出某一行对应的具体数据

</code></pre>
<ul>
<li>创建、插入、修改等等语句：</li>
</ul>
<pre><code>
- (BOOL)executeUpdate:(NSString*)sql, ... //执行更新

</code></pre>
<ul>
<li>关闭数据库</li>
</ul>
<pre><code>
[self.db close]; //关闭数据库

</code></pre>
<p><strong>源码</strong></p>
<pre lang="objc" class="objc"><code>
//&lt;1&gt;使用：（需要FMDatabase *db成员变量）

//创建或打开：FMDataBase类

self.db = [FMDatabase databaseWithPath:fileName];

[self.db open]；

[self.db executeUpdate:@“CREATE TABLE IF NOT EXISTS t_student (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT,age INTEGER)”];

[self.db executeUpdate:@"INSERT INTO t_student(name , age) VALUES(‘admin’,‘10')];

//&lt;2&gt;查询：FMResultSet类

//1.查询

FMResultSet *set = [self.db executeQuery:@"SELECT * FROM t_student;"];

//2.取出数据 即 {type}ForColumnIndex:

while ([set next])

{

  //取出姓名

 NSString *name = [set stringForColumnIndex:1];

  //取出年龄

 int age = [set intForColumnIndex:2];

 NSString *name = [set stringForColumn:@"name"];

 int age = [set intForColumn:@"age"];

 NSLog(@"name = %@, age = %d", name, age);

}

//&lt;3&gt;关闭数据库

[self.db close];

</code></pre>
<h2>
<a id="6选择" class="anchor" href="#6%E9%80%89%E6%8B%A9"></a>6.选择</h2>
<p>既然有这五种存储方案，那么在项目中应该选择哪种是最佳的方式呢，这就涉及到了下面这个问题了。</p>
<p>什么时候用？通俗的说，也就是针对某种业务，这五个存储方式的最佳选择。</p>
<p>针对上面的这个问题，基本是可以有四种参考的维度，分别是：</p>
<ul>
<li>数据量</li>
<li>数据类型</li>
<li>数据载体的形式</li>
<li>数据操作的特点</li>
<li>现在就具体的列表说一下这些区别。</li>
</ul>
<table>
<thead>
<tr>
<th>存储方式</th>
<th>数据量</th>
<th>数据类型</th>
<th>数据载体</th>
<th>数据操作特点</th>
<th>应用示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>plist存储</td>
<td>适合存储小数据量而且属于一类的列表类的数据</td>
<td>只能存储固定的几种类型，NSString、NSArray、NSDictionary、NSData、NSDate、NSNumber、Boolean，不能存储自定义对象</td>
<td>非自定义实例对象、基本数据</td>
<td>直接在可见文件上操作，增删改查很方便</td>
<td>省市列表、表情列表等</td>
</tr>
<tr>
<td>偏好存储</td>
<td>适合存储小数据量而是一般是配置信息类的数据，有时根据需要也会存储一些标志键值数据</td>
<td>可以存储OC定义的所有数据类型</td>
<td>实例对象、基本数据</td>
<td>必须依赖NSUserDefaluts实例对象的实例方法进行存取，过程稍微复杂</td>
<td>App应用程序的信息配置，如版本号、app名称、用户权限等标志键值，如判断启动页、是否自动登录等</td>
</tr>
<tr>
<td>归档存储</td>
<td>适合存储数据量稍微较大的数据</td>
<td>只能存储实现了NSCoding协议和NSCopying协议的实例对象类型。</td>
<td>实例对象</td>
<td>必须依赖NSKeyedUnarchiver、NSKeyedArchiver的类方法或者实例方法进行存取，有时可能还会结合NSUserDefaluts偏好，过程比较复杂</td>
<td>用户的登录/注册信息，如账号、姓名、年龄、学校、省份等</td>
</tr>
<tr>
<td>沙盒存储</td>
<td>适合存储数据量较大的数据</td>
<td>都是存储二进制的NSdata类型</td>
<td>文件File</td>
<td>需要依赖文件管理者NSFileManager将文件写入指定的沙盒目录下、从该目录中再读取文件，过程更复杂一些</td>
<td>图片、音频、视频、文本等，如SDWebImage图片缓存框架</td>
</tr>
<tr>
<td>数据库存储</td>
<td>适合存储大数据量的数据</td>
<td>支持所有的数据类型</td>
<td>实例对象</td>
<td>增删改查方便、快捷，可以任意写sql语句批量处理数据、数据库升级简单等</td>
<td>App中用户浏览过的数据列表内容、电子书读书进度等，基本上大多数app都有本地数据库缓存</td>
</tr>
</tbody>
</table></div>
          </div>
        </div>
      </div>
      </div>
  </div>
</body>
</html>